import json
import subprocess
from dataclasses import dataclass, field
from typing import List, Dict, Optional

@dataclass
class ProbeDisposition:
    default: int
    dub: int
    original: int
    comment: int
    lyrics: int
    karaoke: int
    forced: int
    hearing_impaired: int
    visual_impaired: int
    clean_effects: int
    attached_pic: int
    timed_thumbnails: int
    captions: int
    descriptions: int
    metadata: int
    dependent: int
    still_image: int

@dataclass
class Stream:
    index: int
    codec_name: str
    codec_long_name: str
    codec_type: str
    codec_tag_string: str
    codec_tag: str
    sample_fmt: str
    sample_rate: str
    channels: int
    channel_layout: str
    bits_per_sample: int
    r_frame_rate: str
    avg_frame_rate: str
    time_base: str
    start_pts: int
    start_time: str
    bit_rate: str
    disposition: Optional[ProbeDisposition]
    tags: Dict[str, str]

@dataclass
class Format:
    filename: str
    nb_streams: int
    nb_programs: int
    format_name: str
    format_long_name: str
    start_time: str
    duration: str
    bit_rate: str
    probe_score: int
    tags: Dict[str, str]

@dataclass
class Result:
    format: Format
    streams: List[Stream]

    def has_attached_pic(self) -> bool:
        return any(s.codec_type == "video" for s in self.streams)

    def get_tag_by_key(self, key: str) -> str:
        key = key.lower()
        if key in self.format.tags:
            return self.format.tags[key]
        for stream in self.streams:
            if stream.codec_type == "audio" and key in stream.tags:
                return stream.tags[key]
        return ""

    def get_title(self) -> str:
        return self.get_tag_by_key("title")

    def get_album(self) -> str:
        return self.get_tag_by_key("album")

    def get_artists(self) -> List[str]:
        return [artist.strip() for artist in self.get_tag_by_key("artist").split("/")]

    def has_metadata(self) -> bool:
        return bool(self.get_title() or self.get_album() or self.get_artists())

def probe_reader(ctx, rd) -> Result:
    cmd = subprocess.Popen(
        ["ffprobe", "-v", "quiet", "-print_format", "json", "-show_format", "-show_streams", "-show_error", "pipe:0"],
        stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE
    )
    stdout, stderr = cmd.communicate(input=rd.read())
    if cmd.returncode != 0:
        raise RuntimeError(f"ffprobe error: {stderr.decode()}")
    return Result(**json.loads(stdout.decode()))
